<?php
/**
 * Class Resize
 * Desc: Dùng để thay đổi kích thước ảnh, hỗ trợ các định dạng ảnh PNG, JPG, GIF
 *
 */
class Resize
{
    private $image;
    private $imageResized;
    private $width;
    private $height;

    const EXACT = 1;
    const PORTRAIT = 2;
    const LANDSCAPE = 3;
    const AUTO = 4;
    const CROP = 5;

    public function __construct($filename)
    {
        $this->image = $this->openImage($filename);
        $this->width = imagesx($this->image);
        $this->height = imagesy($this->image);
    }

    /**
     * Gói chức năng thay đổi kích thước ảnh
     * @param $filename
     * @param $newFilename
     * @param $newWidth
     * @param $newHeight
     * @param int $option
     */
    public static function exec($filename, $newFilename, $newWidth, $newHeight, $option = self::AUTO)
    {
        $resize = new Resize($filename);
        $resize->resizeImage($newWidth, $newHeight, $option);
        $resize->saveImage($newFilename);
    }

    /**
     * Mở một file ảnh
     * @param $file
     * @return resource
     * @throws Exception
     */
    protected function openImage($file)
    {
        $imageStringContent = file_get_contents($file);
        $image = @imagecreatefromstring($imageStringContent);
        if ($image)
        {
            return $image;
        }
        else
        {
            throw new Exception('Can not open image ' . $file);
        }
    }

    /**
     * Thực hiện thay đổi kích thước ảnh
     * @param $newWidth
     * @param $newHeight
     * @param int $option
     */
    public function resizeImage($newWidth, $newHeight, $option = self::AUTO)
    {
        $newSize = $this->dimensions($newWidth, $newHeight, $option);
        extract($newSize, EXTR_PREFIX_ALL, 'ns');
        if ($option != self::CROP)
        {
            $this->imageResized = imagecreate($ns_width, $ns_height);
            imagecopyresampled($this->imageResized, $this->image,
                0, 0, 0, 0, $ns_width, $ns_height, $this->width, $this->height);
        }
        else
        {
            $this->crop($ns_width, $ns_height, $newWidth, $newHeight);
        }
    }

    /**
     * Tính chiều rộng của ảnh theo chiều cao mới
     * @param $newHeight
     * @return int
     */
    protected function widthByFixedHeight($newHeight)
    {
        $ratio = $this->width / $this->height;
        return $newHeight * $ratio;
    }

    /**
     * Tính chiều cao của ảnh theo chiều rộng mới
     * @param $newWidth
     * @return int
     */
    protected function heightByFixedWidth($newWidth)
    {
        $ratio = $this->height / $this->width;
        return $newWidth * $ratio;
    }

    /**
     * Tính kích thước mới cho ảnh tự động
     * @param $newWidth
     * @param $newHeight
     * @return array
     */
    protected function autoSize($newWidth, $newHeight)
    {
        if ($this->height < $this->width)
        {
            $width = $newWidth;
            $height = $this->heightByFixedWidth($width);
        }
        elseif ($this->height > $this->width)
        {
            $width = $this->widthByFixedHeight($newHeight);
            $height = $newHeight;
        }
        else
        {
            if ($newHeight < $newWidth)
            {
                $width = $newWidth;
                $height = $this->heightByFixedWidth($newWidth);
            }
            elseif ($newHeight > $newWidth)
            {
                $width = $this->widthByFixedHeight($newHeight);
                $height = $newHeight;
            }
            else
            {
                $width = $newWidth;
                $height = $newHeight;
            }
        }

        return compact('width', 'height');
    }

    /**
     * Tính kích thước xén ảnh
     * @param $newWidth
     * @param $newHeight
     * @return array
     */
    protected function autoCropSize($newWidth, $newHeight)
    {
        $heightRatio = $this->height / $newHeight;
        $widthRatio = $this->width / $newWidth;
        $optimalRatio = ($heightRatio < $widthRatio) ? $heightRatio : $widthRatio;
        $width = $this->width / $optimalRatio;
        $height = $this->height / $optimalRatio;
        return compact('width', 'height');
    }

    /**
     * @param $optimalWidth
     * @param $optimalHeight
     * @param $newWidth
     * @param $newHeight
     */
    protected function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)
    {
        $cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
        $cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
        $this->imageResized = imageCreate($newWidth, $newHeight);
        imagecopyresampled($this->imageResized, $this->image, 0, 0,
            $cropStartX, $cropStartY, $newWidth, $newHeight, $newWidth, $newHeight);
    }

    public function saveImage($path, $quality = "100")
    {
        $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
        $functionToCall = array(
            'jpg' => 'imagejpeg',
            'jpeg' => 'imagejpeg',
            'png' => 'imagepng',
            'gif' => 'imagegif'
        );

        if ($ext == 'png') {
            $scaleQuality = round(($quality / 100) * 9);
            $quality = 9 - $scaleQuality;
        }

        if (array_key_exists($ext, $functionToCall))
        {
            call_user_func($functionToCall[$ext], $this->imageResized, $path, $quality);
        }
        else
        {
            throw new Exception("Extension is not allowed!");
        }
    }

    protected function dimensions($newWidth, $newHeight, $option)
    {
        switch ($option)
        {
            case self::EXACT:
                $width = $newWidth;
                $height = $newHeight;
                break;
            case self::PORTRAIT:
                $width = $this->widthByFixedHeight($newHeight);
                $height = $newHeight;
                break;
            case self::AUTO:
                return $this->autoSize($newWidth, $newHeight);
            case self::CROP:
                return $this->autoCropSize($newWidth, $newHeight);
        }
        return compact('width', 'height');
    }
}